#include "Automation.h"
#include "iaction.h"
#include <QApplication>

Automation::Automation()
    : onlyServe_(NULL)
{
    connect(this,&QTcpServer::newConnection,[=](){
       if(onlyServe_){
           qInfo()<<"incoming refused, only one served.";
           return;
       }
       onlyServe_ = nextPendingConnection();
       incomingInit();
    });
}

Automation::~Automation()
{
    if(this->isListening()){
        this->close();
    }
}

bool Automation::init()
{
    if(false == this->listen(QHostAddress::LocalHost,21206))
    {
        return false;
    }

    return true;
}

void Automation::incomingInit()
{
    Q_ASSERT(onlyServe_);
    connect(onlyServe_,&QTcpSocket::readyRead,[=](){
        packetCache_ += onlyServe_->readAll();
        assemblePack();
    });
    connect(onlyServe_,&QTcpSocket::disconnected,[=](){
        qInfo()<<"serve disconnected.";
        onlyServe_->deleteLater();
        onlyServe_ = NULL;
    });
    connect(onlyServe_, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
        [=](QAbstractSocket::SocketError socketError){
        qInfo()<<"serve error."<<socketError<<":"<<onlyServe_->errorString();
        onlyServe_->deleteLater();
        onlyServe_ = NULL;
    });

}

void Automation::assemblePack()
{
    QByteArray pkg= packetCache_.left(4);
    quint32 len = qFromBigEndian(*(quint32*)pkg.data());
    if(packetCache_.length() >= len+4)
    {
        pkg = packetCache_.mid(4,len);
        packetCache_ = packetCache_.mid(len+4);
        QJsonDocument doc = QJsonDocument::fromBinaryData(pkg);
        if(doc.isObject())
        {
            handleJsonObject(doc.object());
        }
    }
}

void Automation::handleJsonObject(const QJsonObject &jobj)
{
    QString type = jobj.value("type").toString();

    if(type == "chain")
    {
        doChain(jobj);
    }
    else if(type == "stop")
    {
        doStop();
    }
    else if(type == "start")
    {
        doStart();
    }
    else if(type == "pause")
    {
        doPause();
    }
    else if(type == "resume")
    {
        doResume();
    }
}

/*{
    "count":1,
    "start":"id-rnd",
    "actions":[
    {
     "name":"Process",
     "id":"id-rnd",
     "param":{
        "path":"xxx",
        "...":"${password}"
        },
     "next":["id12","id3","id4"]
     "mext":2
    }
    ],
    "input":[{"account":"947415547","password":"asdf"},
        {"account":"523897703","password":"weewsffSErse"}
    ]
}*/
void Automation::doChain(const QJsonObject& jobj)
{
    if(state_ != Stopped) return;
    IAction::freeAllActions();
    Q_ASSERT(idAction_.count() == 0);
    idAction_.clear();
    paramReplaceAction_.clear();

    beginning_ = NULL;
    current_ = NULL;
    curParamLine_ = 0;

    int count = jobj.value("count").toInt();
    QString startid = jobj.value("start").toString();
    QJsonArray actions =jobj.value("actions").toArray();
    foreach(QJsonValue jval, actions)
    {
        QJsonObject aobj = jval.toObject();
        QString name = aobj.take("name").toString();
        QString id = aobj.take("id").toString();
        QJsonObject mobj = aobj.take("param").toObject();
        //QJsonArray mary = aobj.take("next").toArray();
        paramsInput_ = aobj.value("input").toArray();

        IAction* act = IAction::create(name);
        if(act)
        {
            act->setId(id);
            act->setParam(mobj);
            if(idAction_.contains(id)){
                qInfo()<<"conflict id:"<<id<<"action";
                IAction* replaced = idAction_.value(id);
                delete replaced;
            }
            idAction_.insert(id,act);

            //把有需要替换参数的action提取出来.
            QJsonDocument tmpDoc(mobj);
            QByteArray paramBA = tmpDoc.toJson(QJsonDocument::Compact);
            int idx = paramBA.indexOf("${");
            if(idx>=0){
                paramReplaceAction_.push_back(act);
            }
        }
    }

    foreach(const QJsonValue &jval, actions)
    {
        QJsonObject aobj = jval.toObject();
        QString id = aobj.take("id").toString();
        QJsonArray mary = aobj.take("next").toArray();
        IAction* accepted = idAction_.value(id);
        int nxtIdx = 0;
        foreach(QJsonValue nval, mary)
        {
            QString nxt = nval.toString();
            IAction* tobeNext = idAction_.value(nxt);
            accepted->setNext(tobeNext,nxtIdx++);
        }
    }
    IAction* started = idAction_.value(startid);
    if(started == NULL){
        qInfo()<<"no start action!!";
    }
    beginning_ = started;
    if(count != idAction_.count())
    {
        qInfo()<<"error count=" <<count <<", real="<<idAction_.count();
    }
}

//TODO 返回当前状态

void Automation::doStart()
{
    if(state_ != Stopped) return;
    current_ = NULL;
    curParamLine_ = 0;
    if(paramsInput_.count() <= 0){
        //emit sigFinished();

        return;
    }
    state_ = Running;
    progress();
}

void Automation::doPause()
{
    if(state_ != Running) return;
    state_ = Paused;
}

void Automation::doStop()
{
    if(state_ != Running) return;
    state_ = Stopped;
    curParamLine_ = 0;
    paramsInput_ = QJsonArray();
    current_ = NULL;
}

void Automation::doResume()
{
    if(state_ != Paused) return;
    state_ = Running;
    progress();
}

void Automation::progress()
{
    do
    {
        if(current_ == NULL)
        {
            if(!nextAccount())
            {
                //emit sigFinished();
                return;
            }
        }
        int ret = current_->doExec();
        if(ret == ERHold){ //hold
            //emit sigHold(current_->msg());
            qInfo()<<"Hold:"<<current_->msg();
            break;
        }
        else if(ret == ERError){
            //emit sigHalt(current_->msg());
            qInfo()<<"Halt:"<<current_->msg();
            break;
        }
        else{
            current_ = current_->getNext(ret);
            if(current_ == NULL){
                qInfo()<<"param line"<<curParamLine_<<"finished.";
                if(!nextAccount())
                {
                    //emit sigFinished();
                    qInfo()<<"Finished.";
                    return;
                }
            }
        }
        QApplication::processEvents();
    }
    while(current_ && state_ == Running);
}


bool Automation::nextAccount()
{
    if(paramsInput_.count()>=(curParamLine_+1))
    {
        QJsonObject jobj = paramsInput_.at(curParamLine_++).toObject();
        foreach(IAction* act, paramReplaceAction_)
        {
            //把每个参数key拿出来去replace
            QStringList keys = jobj.keys();
            foreach(QString key, keys)
            {
                act->replaceValue(key, jobj.value(key).toString());
            }
        }
        qInfo()<<"next coming param is"<<jobj;
        current_ = beginning_;
        return true;
    }
    else
    {
        return false;
    }

}



